package com.smasher.core.io;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.zip.GZIPOutputStream;


/**
 * @author moyu
 */
public class FileUtil {

    private static final String TAG = "FileUtil";
    private static final char[] fPath = {'/', ':', '?', '*', '|', '<', '>', '\\', '"',};

    public static boolean saveFile(@NonNull File file, @NonNull String content) {
        return saveFile(file, content, "UTF-8");
    }

    public static boolean saveFile(@NonNull File file, @NonNull String content, String charset) {
        // 参数验证
        if (charset == null) {
            Log.e(TAG, "saveFile: 参数不能为空");
            return false;
        }

        try (FileOutputStream fos = new FileOutputStream(file)) {
            byte[] buffer = content.getBytes(charset);
            fos.write(buffer);
            return true;
        } catch (UnsupportedEncodingException e) {
            // 捕获并处理不支持的字符编码异常
            System.err.println("不支持的字符编码: " + charset + ". 错误信息: " + e.getMessage());
            return false;
        } catch (IOException e) {
            // 更详细的日志信息
            System.err.println("保存文件时发生错误: 文件路径=" + file.getAbsolutePath() + ", 错误信息=" + e.getMessage());
            return false;
        }
    }

    public static boolean saveFile(@NonNull File file, @NonNull byte[] data) {
        try (FileOutputStream fos = new FileOutputStream(file)) {
            fos.write(data);
            return true;
        } catch (IOException e) {
            // 更详细的日志信息
            System.err.println("保存文件时发生错误: 文件路径=" + file.getAbsolutePath() + ", 错误信息=" + e.getMessage());
            return false;
        }
    }

    public static boolean saveFile(@NonNull File file, @NonNull InputStream is) {
        byte[] buffer = new byte[1024 * 1024]; // 1 MB 缓冲区

        try (
                InputStream inputStream = is;
                FileOutputStream outputStream = new FileOutputStream(file)
        ) {
            int read;
            while ((read = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, read);
            }
            outputStream.flush();
            return true;
        } catch (IOException e) {
            // 更详细的日志信息
            System.err.println("保存文件时发生错误: 文件路径=" + file.getAbsolutePath() + ", 错误信息=" + e.getMessage());
            return false;
        }
    }

    public static boolean saveFile(@NonNull Bitmap bm, @NonNull String fileName) {
        // 参数验证
        if (fileName.isEmpty()) {
            Log.e(TAG, "saveFile: Bitmap 或文件路径不能为空");
            return false;
        }

        File file = new File(fileName);
        File parentDir = file.getParentFile();
        if (parentDir != null && !parentDir.exists() && !parentDir.mkdirs()) {
            Log.e("FileUtil", "无法创建父目录: " + parentDir.getAbsolutePath());
            return false;
        }

        if (file.exists() && !file.delete()) {
            Log.e("FileUtil", "无法删除现有文件: " + file.getAbsolutePath());
            return false;
        }

        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
            return bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);
        } catch (IOException e) {
            Log.e("FileUtil", "保存文件时发生错误: 文件路径=" + file.getAbsolutePath() + ", 错误信息=" + e.getMessage(), e);
            return false;
        }
    }

    public static String loadFile(@NonNull File file) {
        try {
            return loadFile(file, "UTF-8");
        } catch (Exception e) {
            Log.e("FileUtil", " function load file error", e);
        }
        return null;
    }

    public static String loadFile(@NonNull File file, @NonNull String charset) {
        try {
            byte[] bytes = loadFileBytes(file);
            if (bytes != null) {
                return new String(bytes, charset);
            }
        } catch (Exception e) {
            Log.e("FileUtil", " function load file error", e);
        }
        return null;
    }

    /**
     * 加载文件
     * 加载文件不宜过大，避免产生OutOfMemoryError
     *
     * @param file file
     * @return byte[]
     */
    public static byte[] loadFileBytes(@NonNull File file) {

        // 参数验证
        if (!file.exists()) {
            Log.e(TAG, "loadFileBytes: 文件对象不能为空且必须存在");
            return null;
        }

        byte[] buffer = new byte[8192]; // 8 KB 缓冲区

        try (InputStream is = new FileInputStream(file);
             ByteArrayOutputStream output = new ByteArrayOutputStream()) {

            int read;
            while ((read = is.read(buffer)) != -1) {
                output.write(buffer, 0, read);
            }
            return output.toByteArray();
        } catch (IOException e) {
            // 更详细的日志信息
            Log.e("FileUtil", "加载文件时发生错误: 文件路径=" + file.getAbsolutePath() + ", 错误信息=" + e.getMessage(), e);
            return null;
        }
    }

    /**
     * 加载asset资源
     * 加载资源文件不宜过大，避免产生OutOfMemoryError
     *
     * @param ctx  ctx
     * @param path path
     * @return byte[]
     */
    public static byte[] loadAsset(@NonNull Context ctx, @NonNull String path) {
        // 参数验证
        if (path.isEmpty()) {
            throw new IllegalArgumentException("上下文对象或文件路径不能为空");
        }

        byte[] buffer = new byte[8192]; // 8 KB 缓冲区

        try (
                InputStream is = ctx.getAssets().open(path);
                ByteArrayOutputStream output = new ByteArrayOutputStream()
        ) {
            int read;
            while ((read = is.read(buffer)) != -1) {
                output.write(buffer, 0, read);
            }
            return output.toByteArray();
        } catch (IOException e) {
            // 更详细的日志信息
            Log.e(TAG, "加载资产文件时发生错误: 文件路径=" + path + ", 错误信息=" + e.getMessage(), e);
            return null;
        } catch (OutOfMemoryError e) {
            // 处理 OutOfMemoryError
            Log.e(TAG, "加载资产文件时内存不足: 文件路径=" + path + ", 错误信息=" + e.getMessage(), e);
            return null;
        }
    }

    public static void copyFile(@NonNull File sourceFile, File targetFile, boolean overwrite) throws IOException {
        // 参数验证
        if (targetFile == null) {
            throw new IllegalArgumentException("源文件或目标文件不能为空");
        }

        // 检查源文件是否存在
        if (!sourceFile.exists()) {
            throw new IllegalArgumentException("源文件不存在: " + sourceFile.getAbsolutePath());
        }

        // 检查目标文件是否存在并决定是否覆盖
        if (targetFile.exists()) {
            if (overwrite) {
                if (!targetFile.delete()) {
                    throw new IOException("无法删除现有目标文件: " + targetFile.getAbsolutePath());
                }
            } else {
                return; // 不覆盖，直接返回
            }
        }

        // 确保目标文件的父目录存在
        File parentDir = targetFile.getParentFile();
        if (parentDir != null && !parentDir.exists() && !parentDir.mkdirs()) {
            throw new IOException("无法创建目标文件的父目录: " + parentDir.getAbsolutePath());
        }

        byte[] buffer = new byte[8192]; // 8 KB 缓冲区

        try (InputStream is = new FileInputStream(sourceFile);
             FileOutputStream fos = new FileOutputStream(targetFile)) {

            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            Log.e("FileUtil", "复制文件时发生错误: 源文件=" + sourceFile.getAbsolutePath() + ", 目标文件=" + targetFile.getAbsolutePath() + ", 错误信息=" + e.getMessage(), e);
            throw e; // 抛出异常以便调用者可以处理
        }
    }

    public void copyFile(@NonNull String oldPath, @NonNull String newPath) throws IOException {
        // 参数验证
        if (oldPath.isEmpty() || newPath.isEmpty()) {
            throw new IllegalArgumentException("源文件路径或目标文件路径不能为空");
        }

        File oldFile = new File(oldPath);
        if (!oldFile.exists()) {
            throw new IllegalArgumentException("源文件不存在: " + oldPath);
        }

        byte[] buffer = new byte[8192]; // 8 KB 缓冲区

        try (FileInputStream is = new FileInputStream(oldFile);
             FileOutputStream fos = new FileOutputStream(newPath)) {
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            // 更详细的日志信息
            Log.e("FileUtil", "复制文件时发生错误: 源文件=" + oldPath + ", 目标文件=" + newPath + ", 错误信息=" + e.getMessage(), e);
            throw e; // 抛出异常以便调用者可以处理
        }
    }

    public static void copyFile(@NonNull InputStream is, @NonNull File targetFile) throws IOException {
        // 检查目标文件是否存在
        if (targetFile.exists()) {
            Log.i("FileUtil", "目标文件已存在: " + targetFile.getAbsolutePath());
            return; // 如果文件已存在则直接返回
        }

        // 确保目标文件的父目录存在
        File parentDir = targetFile.getParentFile();
        if (parentDir != null && !parentDir.exists() && !parentDir.mkdirs()) {
            throw new IOException("无法创建目标文件的父目录: " + parentDir.getAbsolutePath());
        }

        byte[] buffer = new byte[8192]; // 8 KB 缓冲区

        try (FileOutputStream fos = new FileOutputStream(targetFile)) {
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            // 更详细的日志信息
            Log.e("FileUtil", "复制文件时发生错误: 目标文件=" + targetFile.getAbsolutePath() + ", 错误信息=" + e.getMessage(), e);
            throw e; // 抛出异常以便调用者可以处理
        } finally {
            try {
                is.close(); // 关闭输入流
            } catch (IOException e) {
                Log.e("FileUtil", "关闭输入流时发生错误: " + e.getMessage(), e);
            }
        }
    }

    public static void copyAssetsFile(@NonNull Context ctx, @NonNull String assetsPath, File targetFile) {
        try (InputStream is = ctx.getResources().getAssets().open(assetsPath)) {
            copyFile(is, targetFile);
        } catch (IOException e) {
            Log.e("FileUtil", " function save file error", e);
        }
    }

    public static File createFile(@NonNull String fileName, boolean isFile) {
        // 参数验证
        if (fileName.isEmpty()) {
            throw new IllegalArgumentException("文件名不能为空");
        }

        File file = new File(fileName);

        // 如果文件存在且是文件，而我们想要创建文件，则删除现有文件
        if (file.exists() && file.isFile() && isFile) {
            if (!file.delete()) {
                Log.w(TAG, "无法删除现有文件: " + fileName);
            }
        }

        // 确保父目录存在
        File parentDir = file.getParentFile();
        if (parentDir != null && !parentDir.exists() && !parentDir.mkdirs()) {
            throw new RuntimeException("无法创建目标文件的父目录: " + parentDir.getAbsolutePath());
        }

        // 创建文件或目录
        if (!file.exists()) {
            try {
                if (isFile) {
                    if (!file.createNewFile()) {
                        Log.w(TAG, "文件已存在或创建失败: " + fileName);
                    }
                } else {
                    if (!file.mkdirs()) {
                        Log.w(TAG, "目录已存在或创建失败: " + fileName);
                    }
                }
            } catch (IOException e) {
                Log.e(TAG, "创建文件时发生错误: " + fileName + ", 错误信息=" + e.getMessage(), e);
                throw new RuntimeException("创建文件失败", e);
            }
        }
        return file;
    }

    public static void createFile(@NonNull File file, boolean isFile) {

        // 确保父目录存在
        File parentDir = file.getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            createParentDirs(parentDir);
        }

        // 如果文件不存在则尝试创建
        if (!file.exists()) {
            try {
                if (isFile) {
                    if (!file.createNewFile()) {
                        Log.w(TAG, "文件已存在或创建失败: " + file.getAbsolutePath());
                    }
                } else {
                    if (!file.mkdirs()) {
                        Log.w(TAG, "目录已存在或创建失败: " + file.getAbsolutePath());
                    }
                }
            } catch (IOException e) {
                Log.e(TAG, "创建文件时发生错误: " + file.getAbsolutePath() + ", 错误信息=" + e.getMessage(), e);
                throw new RuntimeException("创建文件失败", e);
            }
        }
    }


    /**
     * 递归创建所有必要的父目录。
     *
     * @param dir 目标父目录
     */
    private static void createParentDirs(File dir) {
        File parent = dir.getParentFile();
        if (parent != null && !parent.exists()) {
            createParentDirs(parent);
        }
        if (!dir.exists() && !dir.mkdirs()) {
            throw new RuntimeException("无法创建父目录: " + dir.getAbsolutePath());
        }
    }

    public static boolean deleteFile(@NonNull String fileName) {
        if (TextUtils.isEmpty(fileName)) {
            // Log the error or handle it as needed
            return false;
        }

        File file = new File(fileName);
        try {
            if (file.exists()) {
                if (file.isFile()) { // Ensure it's a file, not a directory
                    if (!file.canWrite()) {
                        // Log lack of write permission or handle it as needed
                        return false;
                    }
                    return file.delete();
                } else {
                    // Log attempt to delete a directory or handle it as needed
                    return false;
                }
            } else {
                // Log file does not exist or handle it as needed
                return false;
            }
        } catch (SecurityException e) {
            // Log security exception or handle it as needed
            Log.e(TAG, "deleteFile: ", e);
            return false;
        }
    }

    public static boolean deleteFolderFile(@NonNull String filePath, boolean deleteThisPath) {

        if (TextUtils.isEmpty(filePath)) {
            return false;
        }

        File file = new File(filePath);
        if (!file.exists()) {
            return false;
        }

        // 如果是目录，则递归删除所有子文件和子目录
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {  // 防止 listFiles() 返回 null（例如，如果这是一个空目录）
                for (File subFile : files) {
                    // 注意：这里应该检查 deleteFolderFile 的返回值，确保所有文件都被成功删除。
                    if (!deleteFolderFile(subFile.getAbsolutePath(), true)) {
                        return false;  // 如果任何一个文件或子目录删除失败，则整个操作失败。
                    }
                }
            }
        }

        // 如果需要删除此路径本身，并且它不是一个非空目录，则尝试删除它。
        if (deleteThisPath) {
            if (file.isFile()) {
                return file.delete();
            } else if (file.isDirectory()) {
                String[] files = file.list();
                // Check for null to prevent NullPointerException.
                if (files == null || files.length == 0) {
                    // Directory is either not readable or empty, attempt to delete.
                    return file.delete();
                }
            }
        }

        // 如果不需要删除路径本身，或者它是非空目录，则视为成功。
        String[] files = file.list();
        // Check for null to prevent NullPointerException.
        return !deleteThisPath || files == null || files.length > 0;

    }

    public static boolean deleteAllFiles(@NonNull File root) {
        if (!root.exists()) return false;

        // If it's a file, delete it directly.
        if (root.isFile()) {
            return root.delete();
        }

        // If it's a directory, delete all files and subdirectories.
        File[] files = root.listFiles();
        if (files != null) {
            for (File file : files) {
                // Recursively delete each file or directory.
                if (!deleteAllFiles(file)) {
                    return false; // If deletion fails for any file, return false.
                }
            }
        }

        // Finally, attempt to delete the directory itself.
        return root.delete();

    }

    public static File findFirstFileByExtension(File dir, final String extension) {
        if (!dir.exists()) {
            return null;
        }
        if (!dir.isDirectory()) {
            return null;
        }
        File[] files = dir.listFiles((dir1, fileName) ->
                fileName != null && fileName.toLowerCase().endsWith(extension));

        if (files == null || files.length == 0) {
            return null;
        }
        return files[0];
    }

    public static File[] getFilesFromDir(String dirName) {
        File file = new File(dirName);
        if (file.exists()) {
            return file.listFiles();
        }
        return null;
    }

    /**
     * 从给定的输入流中读取4个字节的数据，并按照小端字节序（little-endian）将其解释为一个32位整数。
     *
     * @param dis 输入流，从中读取数据。
     * @return 按照小端字节序解释的32位整数值。
     * @throws IOException 如果在读取过程中发生输入输出错误。
     *                     注意：该方法假设可以成功读取到4个字节。如果输入流中的可用字节数少于4个，
     *                     或者在读取过程中遇到其他问题，则会抛出IOException。
     */
    public static int readLittleEndianInt(InputStream dis) throws IOException {
        byte[] bytes = new byte[4];
        int count = dis.read(bytes);
        ByteBuffer bytebuffer = ByteBuffer.wrap(bytes);
        bytebuffer.order(ByteOrder.LITTLE_ENDIAN);
        return bytebuffer.getInt();
    }

    /**
     * 从给定的输入流中读取8个字节的数据，并按照小端字节序（little-endian）将其解释为一个64位长整数。
     *
     * @param dis 输入流，从中读取数据。
     * @return 按照小端字节序解释的64位长整数值；如果流已结束，则返回-1。
     * @throws IOException 如果在读取过程中发生输入输出错误。
     *                     注意：
     *                     - 如果输入流中的可用字节数少于8个，则此方法的行为未定义。建议检查读取的字节数。
     *                     - 返回值-1既可能是流结束的标志，也可能是合法的数据值。应根据实际情况选择合适的处理逻辑。
     */
    public static long readLittleEndianLong(InputStream dis) throws IOException {
        byte[] bytes = new byte[8];
        int re = dis.read(bytes);
        if (re == -1) {
            return -1;
        }
        ByteBuffer bytebuffer = ByteBuffer.wrap(bytes);
        bytebuffer.order(ByteOrder.LITTLE_ENDIAN);
        return bytebuffer.getLong();
    }

    public static void scanDirAsync(Context ctx, String dir) {
        Intent scanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_DIR");
        scanIntent.setData(Uri.fromFile(new File(dir)));
        ctx.sendBroadcast(scanIntent);
    }

    public static void copyAllFile(String oldPath, String newPath) {


        try {
            File newFile = new File(newPath);
            if (!newFile.mkdirs()) {
                // 创建目标目录
                Log.i(TAG, "mkdirs failed ");
            }

            File sourceDir = new File(oldPath);
            if (!sourceDir.exists() || !sourceDir.isDirectory()) {
                Log.e(TAG, "Source directory does not exist or is not a directory: " + oldPath);
                return;
            }

            String[] files = sourceDir.list();
            if (files == null) {
                return;
            }

            for (String fileName : files) {
                File temp = new File(sourceDir, fileName);
                File destFile = new File(newPath, fileName);

                if (temp.isFile()) {
                    // 使用try-with-resources自动管理流的关闭
                    try (FileInputStream input = new FileInputStream(temp);
                         FileOutputStream output = new FileOutputStream(destFile)) {

                        byte[] buffer = new byte[1024]; // 增加缓冲区大小以提高性能
                        int length;
                        while ((length = input.read(buffer)) > 0) {
                            output.write(buffer, 0, length);
                        }
                    } catch (IOException e) {
                        Log.e(TAG, "copyAllFile: ", e);
                    }
                } else if (temp.isDirectory()) {
                    // 递归复制子目录
                    copyAllFile(temp.getAbsolutePath(), destFile.getAbsolutePath());
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "copyAllFile: ", e);
        }

    }

    /**
     * 获取指定路径下的文件
     *
     * @param filePath filePath
     * @return File
     */
    public static File getAccessFileOrCreate(String filePath) {

        File file = new File(filePath);
        try {
            // 确保父目录存在
            if (mkdirsIfNotExit(file.getParentFile())) {
                // 如果文件不存在，则创建
                if (!file.exists() && !file.createNewFile()) {
                    Log.w("FileUtil", "Failed to create file: " + filePath);
                }
            }
            return file;
        } catch (IOException e) {
            Log.e("FileUtil", "Error getting or creating file: " + filePath, e);
        }
        return null;
    }

    /**
     * 如果父目录不存在，则创建它们。
     *
     * @param parentDir 父目录
     * @return 如果成功创建或者已经存在返回true，否则返回false
     */
    public static boolean mkdirsIfNotExit(File parentDir) {
        if (parentDir == null || parentDir.exists()) {
            return true; // 已经存在或无需创建（如文件位于根目录）
        }
        return parentDir.mkdirs();
    }

    /**
     * 创建目录
     *
     * @param dirName 目录名
     * @return 是否创建成功
     */
    public static boolean makeDir(String dirName) {
        File dir = new File(dirName);
        if (!dir.exists()) {
            return dir.mkdirs();
        }
        return true;
    }


    /**
     * 追加内容到文件
     *
     * @param file    文件
     * @param content 内容
     * @return 是否成功
     */
    public static boolean appendFile(@NonNull File file, String content) {
        try (FileOutputStream fos = new FileOutputStream(file, true)) {
            byte[] buffer = content.getBytes();
            fos.write(buffer);
            return true;
        } catch (IOException e) {
            Log.e("FileUtil", " function append file error", e);
            return false;
        }
    }

    /**
     * gzip压缩文件
     *
     * @param fromFile fromFile
     * @param toFile   toFile
     */
    public static void gzipFile(@NonNull File fromFile, @NonNull File toFile) throws IOException {

        if (!fromFile.exists()) {
            throw new IllegalArgumentException("Source file does not exist: " + fromFile.getAbsolutePath());
        }

        try (FileInputStream in = new FileInputStream(fromFile);
             GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(toFile))) {

            byte[] buffer = new byte[8192]; // 使用更大的缓冲区以提高性能
            int len;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }

        } catch (IOException e) {
            throw new IOException("Failed to gzip file: " + fromFile.getAbsolutePath(), e);
        }
    }
}
